﻿using System.Collections.Generic;
using System.Linq;

namespace AutomatedDecisionMaking.Class.Algorithms
{
    class BayesAlgorithm
    {
        private readonly DecisionMatrix _mDecisionMatrix;                   // Macierz decyzji
        private List<double> _mScenarioWeights;                             // Wagi poszczególnych scenariuszy
        private List<List<double>> _mContitionWeights;                      // Wagi warunków dla poszczególnych scenariuszy
        private readonly List<List<List<double>>> _mConditionTableList;     // Lista tablic bayesa dla poszczególnych warunków 
        private readonly List<List<double>> _mWeightedValues;               // Lista wartości ważonych dla poszczególnych warunków
        private readonly List<List<string>> _mWeightedValuesStr;            // Lista wartości ważonych dla poszczególnych warunków w odpowiednim formacje
        private readonly List<double> _mMaxWeightedVals;                    // Maksymalne wartości ważone dla poszczególnych warunków
        private readonly List<string> _mDecisions;                          // Decyzje dla poszczególnych warunków
        
        /// <summary>
        /// Konstruktor algorytmu realizującego analizę Bayesowską.
        /// </summary>
        /// <param name="decisionMatrix">Macierz decyzji wprowadzona porzez użytkownika</param>
        public BayesAlgorithm(DecisionMatrix decisionMatrix)
        {
            _mScenarioWeights = new List<double>();
            _mContitionWeights = new List<List<double>>();
            _mConditionTableList = new List<List<List<double>>>();
            _mWeightedValues = new List<List<double>>();
            _mWeightedValuesStr = new List<List<string>>();
            _mMaxWeightedVals = new List<double>();
            _mDecisions = new List<string>();
            _mDecisionMatrix = decisionMatrix;
        }

        /// <summary>
        /// Reset algorytmu
        /// </summary>
        public void Reset()
        {
            _mScenarioWeights.Clear();
            _mContitionWeights.Clear();
            _mConditionTableList.Clear();
            _mWeightedValues.Clear();
            _mWeightedValuesStr.Clear();
            _mMaxWeightedVals.Clear();
            _mDecisions.Clear();
        }

        /// <summary>
        /// Uruchomienie algorytmu i rozwiązanie postawionego zadania
        /// </summary>
        public void CalculateSolution()
        {
            CalculateConditionTable();
            int conditionsTablesNo = _mContitionWeights.Count;
            int scenarioNo = _mDecisionMatrix.ScenarioList.Count;

            for (int i = 0; i < conditionsTablesNo; ++i)
            {
                var weightedValueStr = new List<string>();
                var weightedValue = new List<double>();
                for (int j = 0; j < scenarioNo; ++j)
                {
                    string element = _mDecisionMatrix.DecisionList.ElementAt(j);
                    double weightedVal = 0;
                    //var conditionRow = _mConditionTableList[i][j];
                    for (int k = 0; k < scenarioNo; ++k)
                        weightedVal += _mConditionTableList[i][k][3]*_mDecisionMatrix.Value[j][k];

                    weightedValueStr.Add(element + " : wartość ważona = " + weightedVal);
                    weightedValue.Add(weightedVal);
                }
                _mWeightedValuesStr.Add(weightedValueStr);
                _mWeightedValues.Add(weightedValue);
            }

            for (int i = 0; i < conditionsTablesNo; ++i)
            {
                _mMaxWeightedVals.Add(_mWeightedValues[i].Max());
                _mDecisions.Add(_mDecisionMatrix.DecisionList.ElementAt(_mWeightedValues[i].FindIndex(w => w == _mMaxWeightedVals[i])));
            }
        }

        /// <summary>
        /// Wyznaczenie tablic warunkowych
        /// </summary>
        private void CalculateConditionTable()
        {
            int conditionsTablesNo = _mContitionWeights.Count;
            int scenarioNo = _mDecisionMatrix.ScenarioList.Count;

            
            for(int i=0; i<conditionsTablesNo; ++i)
            {
                var conditionTable = new List<List<double>>();
                for(int j=0; j<scenarioNo; ++j)
                {
                    var row = new List<double>();
                    
                    double scenarioWeight = _mScenarioWeights[j];
                    double conditionWeight = _mContitionWeights[i][j];
                    row.Add(scenarioWeight);
                    row.Add(conditionWeight);
                    row.Add(scenarioWeight * conditionWeight);
                    
                    conditionTable.Add(row);
                }
                
                double probabilitySum = 0;
                for (int j = 0; j < scenarioNo; ++j)
                    probabilitySum += conditionTable[j][2];
                
                for (int j = 0; j < scenarioNo; ++j)
                    conditionTable[j].Add(conditionTable[j][2]/probabilitySum);

                _mConditionTableList.Add(conditionTable);
            }
        }

        /// <summary>
        /// Ustawienie wag dla poszczególnych warunków
        /// </summary>
        /// <param name="weights">Wagi dla poszczególnych warunków</param>
        public void SetConditionWeights(List<List<double>> weights)
        {
            _mContitionWeights = weights;
        }

        /// <summary>
        /// Pobranie wag dla poszczególnych warunków
        /// </summary>
        /// <returns>Wagi dla poszczególnych warunków</returns>
        public List<List<double>> GetConditionWeights()
        {
            return _mContitionWeights;
        }

        /// <summary>
        /// Ustawienie wag dla poszczególnych scenariuszy
        /// </summary>
        /// <param name="weights">Wagi dla scenariuszy</param>
        public void SetScenarioWeights(List<double> weights)
        {
            _mScenarioWeights = weights;
        }

        /// <summary>
        /// Pobranie wag dla poszczególnych scenariuszy
        /// </summary>
        /// <returns>Wagi dla scenariuszy</returns>
        public List<double> GetScenarioWeights()
        {
            return _mScenarioWeights;
        }

        /// <summary>
        /// Pobranie tablicy warunków czyli tablic z analizą Bayes'a
        /// </summary>
        /// <returns>Tablise warunkowe Bayes'a</returns>
        public List<List<List<double>>> GetConditionTableList()
        {
            return _mConditionTableList;
        }

        /// <summary>
        /// Pobranie wartości ważonych dla poszczególnych warunków
        /// </summary>
        /// <returns>Wartości ważone dla poszczególncyh warunków</returns>
        public List<List<string>> GetWeightedValuesStr()
        {
            return _mWeightedValuesStr;
        }

        /// <summary>
        /// Pobranie wartości ważonych dla poszczególnych warunków
        /// </summary>
        /// <returns>Wartości ważone dla poszczególncyh warunków</returns>
        public List<List<double>> GetWeightedValues()
        {
            return _mWeightedValues;
        }

        /// <summary>
        /// Pobranie decyzji dla poszczególnych warunków
        /// </summary>
        /// <returns>Podjęte decyzje dla poszczególnych warunków</returns>
        public List<string> GetDecisions()
        {
            return _mDecisions;
        }

        /// <summary>
        /// Pobranie listy wartości odpowiadającyh podjętym decyzją
        /// </summary>
        /// <returns>Wartości odpowiadające podjętym decyzjom</returns>
        public List<double> GetMaxWeightedVals()
        {
            return _mMaxWeightedVals;
        }

        /// <summary>
        /// Macierz decyzji
        /// </summary>
        /// <returns>Macierz decyzji</returns>
        public DecisionMatrix GetDecisionMatrix()
        {
            return _mDecisionMatrix;
        }
    }
}
